home *** CD-ROM | disk | FTP | other *** search
/ Light ROM 3 / Light ROM 3 - Disc 2.iso / programs / amiga / misc / terrain3.lzh / atv3d.c < prev    next >
C/C++ Source or Header  |  1987-08-23  |  20KB  |  529 lines

  1.  
  2. #include "atv3d.h"
  3.  
  4. extern struct Requester req;           /* custom requester structure */ 
  5. extern struct Border out_border;       /* they must have at least one */
  6. extern struct Gadget ongad;            /* the on gadget is the TRUE one */
  7. extern struct Gadget offgad;           /* the off gadget is the FALSE one */
  8.  
  9. extern short autoEnquire();            /* declare the trackable requester */
  10.  
  11. /*
  12.  * sc.c - fractalish terrain generator.
  13.  *
  14.  *   Date:     March 5, 1987 (original version sometime in 1985)
  15.  *   Author:   Chris Gray
  16.  *   Language: C
  17.  *   System:   Amiga
  18.  */
  19.  
  20. /*
  21.  * Badly hacked for 3d and Dynamic allocation under 16-bit integer Manx:
  22.  *
  23.  *   Date:        July 26, 1987
  24.  *   Barbarian:   Howard Hull
  25.  *   Implements:  (some) Dynamic allocation code from Ray Bovet, and
  26.  *                (considerable) custom requester code from John Draper's
  27.  *                 gadget tutorial.
  28.  *                Devil's Post Pile relief generator whipped up by yours
  29.  *                 truly on the spot.
  30.  *
  31.  *                 Structured design?  Structured design is for lords and
  32.  *                 prima donas.  The rest of us have to make do, even the
  33.  *                 geniuses among us:
  34.  *                 "Hold up! Spare me that nonsense. It's dangerous. We're
  35.  *                  set up for direct current in America. People like it,
  36.  *                  and it's all I'll ever fool with. But maybe I could
  37.  *                  give you a job. Can you fix a ship's lighting plant?"
  38.  *                  Thomas Edison to Nikola Tesla, 1884.
  39.  */
  40.  
  41. /*
  42.  * Gray: (continued, but edited for current content)
  43.  *
  44.  * The nature of the terrain can be changed by playing with the numbers
  45.  * in 'Range'. If you change SIZE, you must also change the number of
  46.  * values given for 'Range'.  Also, if you use the graphics HIRES flag
  47.  * to get 640 pixel width screens, it is advisable to use numbers that
  48.  * are approximately half what give good results for 320 pixel width
  49.  * screens.  The way this is set up, the created terrain for SIZE = 8
  50.  * and XGEOM set to SINGLES is 256 pixels by 256 pixels, which doesn't
  51.  * fit on a non-interlaced screen.  Only the top 200 pixels are shown.
  52.  * The terrain is a torus, i.e. wraps around both horizontally as well as
  53.  * vertically.
  54.  *
  55.  * In this modified program, to see the whole torus in a rectangular
  56.  * format, use SIZE = 7 and set XGEOM to DOUBLES, YGEOM to SINGLES.
  57.  * For other combinations of SIZE, X and Y GEOM, Range, SCREEN_HEIGHT
  58.  * and SCREEN_WIDTH, DEPTH and ColourMap, see instructions
  59.  * below.  One of the more outrageous combinations, 256 wide by 400 high,
  60.  * (384 pixels plus 16 for terrain relief), with 16 colors, can be set up
  61.  * by using SIZE = 7, XGEOM = DOUBLES, YGEOM = TRIPLES, 7 value Range table,
  62.  * SCREEN_HEIGHT = 400,  SCREEN_WIDTH = 320, DEPTH = 4, use the sixteen
  63.  * element ColourMap, and set NewWindow flag = LACE.  This is also the
  64.  * fastest Devil's Post Pile configuration, since the posts are shorter.
  65.  * The disadvantage is that the scale of the terrain is smaller.
  66.  */
  67.  
  68. /*
  69.  * Gray goes on to add:
  70.  * Feel free to use this algorithm in any games you write, so long as you
  71.  * give me [Gray] credit for it. (I THINK I invented it, since I've never
  72.  * heard of anything similar, and other programs I've seen use much slower
  73.  * methods.)
  74.  */
  75.  
  76. /*
  77.  * Of course, by adding the 3d algorithm to Gray's code (that has a familiar
  78.  * ring, somehow) things did get somewhat slower, so it's a good thing that
  79.  * Gray decided to make the code fast!
  80.  */
  81.  
  82. #define SINGLES 1           /* Use to obtain normal pixel map              */
  83. #define DOUBLES 2           /* Use to obtain wide rectangular pixel map    */
  84. #define TRIPLES 3           /* Use to obtain wider rectangular pixel map   */
  85. #define QUADRUS 4           /* Use to obtain widest rectangular pixel map  */
  86. #define YGEOM  SINGLES      /* Selects picture Y geometry.  Use with LACE  */
  87. #define XGEOM  SINGLES      /* Selects picture X geometry.  Use with HIRES */
  88.                             /* Usually YGEOM is SINGLES, w XGEOM as below: */
  89. #define SIZE 8              /* 128 X 128 SIZE 7 w SINGLES, or 256 X 128 at */
  90. #define MAP (1 << SIZE)     /* 7 in DOUBLES.  MAP is edge base length in   */
  91. #define MAPH (MAP*YGEOM)    /* pixels, 128 for SIZE = 7, 256 for SIZE = 8  */
  92. #define MAPW (MAP*XGEOM)    /* configured as torus; GEOM's extend this.    */
  93. #define CAMOD 4             /* SIZE 7 always 4, SIZE 8 w DOUBLES must be 8 */
  94. #define MAPMH MAPH/CAMOD    /* sets calloc module size, 16-bit calloc      */
  95. #define CSIZE 2             /* calloc needs to know short type is 2 bytes  */
  96. #define SCREEN_WIDTH 320    /* 320X200: 7 & SINGLES 32K, looks 1/4 size    */
  97. #define SCREEN_HEIGHT 400   /* 320X200: 7 & DOUBLES 65K, 8 & SINGLES 131K  */
  98.                             /* 320X400: 7 & TRIPLES 98K, 8 & SINGLES 131K  */
  99.                             /* 640X200: 8 & SINGLES 131K, 7 & QUADRUS 131K */
  100.                             /* 640X200: 8 & DOUBLES 262K, 8 & QUADRUS 393K */
  101.                             /* 640X400: 8 & DOUBLES 262K, 8 & TRIPLES 393K */
  102.                             /* and so on.  The possiblities are endless... */
  103. #define SCREEN_DEPTH 5L     /* use 4L max w HIRES flag, 5L max w LACE flag */
  104. #define COLOURS (long)(1 << SCREEN_DEPTH)   /* set according to HIRES flag */
  105. #define WINDOW_WIDTH (long)(MAPW < SCREEN_WIDTH ? MAPW : SCREEN_WIDTH)
  106. #define WINDOW_LENGTH (long)(MAPH + COLOURS < SCREEN_HEIGHT ? MAPH + COLOURS : SCREEN_HEIGHT)
  107. #define WINDOW_HEIGHT (long)(MAPH < SCREEN_HEIGHT ? MAPH : SCREEN_HEIGHT)
  108. #define REQ_WIDTH 64L               /* Width of enquirer window            */
  109. #define REQ_HEIGHT 72L              /* Length of enquirer window           */
  110. #define REQ_LEFT_EDGE (long)(SCREEN_WIDTH - REQ_WIDTH) /* enquirer window  */
  111. #define REQ_TOP_EDGE 12L            /* initial position on terrain screen  */
  112.  
  113. #define FREEPEN (-1)
  114.  
  115. /* Range for use with SIZE = 7  and 4 bit planes */
  116. /* static unsigned short Range[SIZE] = {20, 18, 18, 5, 4, 3, 2}; */
  117.  
  118. /* Range for use with SIZE = 7  and 5 bit planes */
  119. /* static unsigned short Range[SIZE] = {36, 34, 34, 8, 6, 4, 2}; */
  120.  
  121. /* Range for use with SIZE = 8  and 4 bit planes */
  122. /* static unsigned short Range[SIZE] = {20, 18, 18, 18, 5, 4, 3, 2}; */
  123.  
  124. /* Range for use with SIZE = 8  and 5 bit planes */
  125.    static unsigned short Range[SIZE] = {36, 34, 34, 34, 8, 6, 4, 2};
  126.  
  127. /* Colour table for non-HIRES screen */
  128.    static unsigned short ColourMap[COLOURS] = {
  129.     0x00f, 0x0ff, 0x765, 0x0b0, 0x0d0, 0x0f0, 0x4f4, 0x8fb,
  130.     0xdfa, 0xff9, 0xbd8, 0x9c7, 0x6b5, 0x4a6, 0x297, 0x088,
  131.     0x078, 0x177, 0x366, 0x465, 0x663, 0x762, 0x951, 0xa50,
  132.     0xa62, 0xa74, 0xa86, 0xa98, 0xaaa, 0xccc, 0xeee, 0xfff
  133. };
  134.  
  135. /* Colour table for HIRES screen */
  136. /* static unsigned short ColourMap[COLOURS] = {
  137.     0x00f, 0x0ff, 0x765, 0x0f0,
  138.     0x8fb, 0xff9, 0x9c7, 0x4a6,
  139.     0x088, 0x177, 0x465, 0x762,
  140.     0xa50, 0xa74, 0xa98, 0xfff
  141. }; */
  142.  
  143. /* AutoEnquire body and button texts */
  144. struct IntuiText negtext =
  145.     {COLOURS - 1L, 0L, JAM2, 5L, 2L, NULL, NULL, NULL};
  146. struct IntuiText postext =
  147.     {COLOURS - 1L, 0L, JAM2, 5L, 2L, NULL, NULL, NULL};
  148. struct IntuiText text =
  149.     {COLOURS - 1L, 0L, JAM2, 12L, 7L, NULL, NULL, NULL};
  150.  
  151. short wleftedge = -1; /* These are the variables in which we     */
  152. short wtopedge = -1;  /* get the first moved enquirer position.  */
  153.  
  154. static unsigned short Seed;
  155. static short altitude;
  156. short interested;
  157.  
  158. short *Cell[MAPH];  /* define an array of pointers to modules from calloc */
  159. char *calloc();     /* define the allocator subroutine call */
  160.  
  161. /*
  162.  * random - return a random number 0 - passed range.
  163.  */
  164.  
  165. unsigned short random(rang)
  166. unsigned short rang;
  167. {
  168.     if (rang == 0)
  169.         return 0;
  170.     Seed = Seed * 17137 + 4287;
  171.     Seed = (Seed >> 8) ^ (Seed << 8);
  172.     return Seed % rang;
  173. }
  174.  
  175. /*
  176.  * set - set a given spot in Cell.
  177.  */
  178.  
  179. void set(size, height)
  180. unsigned short size;
  181. short height;
  182. {
  183.     unsigned short rang;
  184.  
  185.     rang = Range[size];
  186.     height = height + random(rang) - (rang + 1) / 2;
  187.     altitude = height;
  188. }
  189.  
  190. /*
  191.  * bound - set colour 0 at image edge.
  192.  */
  193.  
  194. void bound(l)
  195. unsigned short l;
  196. {
  197.     unsigned short c;
  198.     short height;
  199.  
  200.     height = 0;
  201.     for (c=0; c<WINDOW_WIDTH; c++)
  202.         {
  203.         SetAPen(Window->RPort, (long)height);
  204.         WritePixel(Window->RPort, (long)c, (long)l);
  205.     }
  206. }
  207.  
  208. /*
  209.  * show - set a Devil's Post Pile (line of pixels) in the terrain window.
  210.  */
  211.  
  212. void show(cc, l, Cell)
  213. unsigned short cc, l;
  214. short *Cell;
  215. {
  216.     unsigned short c, ll0;
  217.     short ll, lll, height;
  218.     short colour = 0;
  219.  
  220.     for (c=0; c<WINDOW_WIDTH; c++)
  221.         {
  222.         height = Cell[c];
  223.         if (height < 0) height = 0;
  224.         if (height >= COLOURS) height = COLOURS - 1;
  225.         ll0 = l + COLOURS -1;
  226.         ll = ll0 - height;
  227.         colour = height;
  228.         if (cc != 0 && height != 0)  colour = cc;
  229.         for (lll = ll0; lll >= ll; lll -= 1) 
  230.             {
  231.             SetAPen(Window->RPort, (long)colour);
  232.             WritePixel(Window->RPort, (long)c, (long)lll);
  233.         }
  234.     }
  235. }
  236.  
  237. struct IntuitionBase *IntuitionBase;
  238. struct GfxBase       *GfxBase;
  239.  
  240. /*
  241.  * main program.
  242.  */
  243.  
  244. void main()
  245. {
  246.  
  247.     unsigned short i, j, k, l, m;
  248.     unsigned short c, step, nextStep, l1, l2, c1, c2, cc;
  249.     short height, ll, ll0, lll;
  250.  
  251.     struct DateStamp ds;
  252.  
  253. /* this is the terrain screen opening structure.  Configure as per RKM */
  254. /* flags field may contain 0x0, LACE, or HIRES */
  255.     static struct NewScreen newScreen =
  256.             {
  257.             0L, 0L, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH, 0L, 1L,
  258.             LACE, CUSTOMSCREEN, NULL, NULL, NULL, NULL
  259.             };
  260.  
  261. /* this is the terrain display's window opening structure */
  262.     static struct NewWindow newWindow =
  263.             {
  264.             0L, 0L, WINDOW_WIDTH, WINDOW_LENGTH,
  265.             FREEPEN, FREEPEN,
  266.             0x0, BORDERLESS | ACTIVATE | NOCAREREFRESH,
  267.             NULL, NULL, NULL, NULL, NULL, 0L, 0L, 0L, 0L,
  268.             CUSTOMSCREEN
  269.             };
  270.  
  271. /* this is the AutoEnquire window structure */
  272.     static struct NewWindow reqWindow =
  273.             {
  274.             REQ_LEFT_EDGE, REQ_TOP_EDGE, REQ_WIDTH, REQ_HEIGHT,
  275.             FREEPEN, FREEPEN,
  276.               CLOSEWINDOW            /*  IDCMP flags                      */
  277.             | REFRESHWINDOW
  278.             | MOUSEBUTTONS
  279.             | REQCLEAR 
  280.             | GADGETDOWN 
  281.             | SELECTDOWN 
  282.             | SELECTUP, 
  283.                                      /*  Usual flags for gadgets and such */ 
  284.               ACTIVATE
  285.             | WINDOWDEPTH 
  286.             | WINDOWDRAG 
  287.             | SMART_REFRESH, 
  288.             NULL, NULL, NULL, NULL, NULL, 0L, 0L, 0L, 0L,
  289.             CUSTOMSCREEN
  290.             };
  291.  
  292. /* 'main' assignments begin here */
  293.  
  294. /* **************************************************************************
  295. *                                                                           *
  296. *  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::                *
  297. *  ::  When you're up to your OS in allocators, it's hard ::                *
  298. *  :: to remember that the task was to empty /dev/swap... ::                *
  299. *  :::::::::::::::::::::::::::::::::::::::::::::::::::::::::                *
  300. *                                                                           *
  301. *  I ransacked some of Ray Bovet's Atari code to get the dynamic allocator. *
  302. *  However, on my own I discovered Horror #1:                               *
  303. *                                                                           *
  304. *  Dynamic allocation for Manx uses either of two Unix compatible routines, *
  305. *  malloc or calloc from the system independent library.  Since calloc      *
  306. *  merely multiplies its two input arguments and uses them to call malloc,  *
  307. *  with a 16-bit integer compiler the maximum request is 65,535 bytes.      *
  308. *  Requests for 65,536 bytes get zero bytes allocated.  In order to obtain  *
  309. *  262,144 bytes we would have to ask malloc for eight 32,768 byte modules. *
  310. *                                                                           *
  311. *  Just to be decent, this code was issued with #defines at the front that  *
  312. *  will ask (as printed) for four modules.  Change the #define for CAMOD to *
  313. *  experiment with this.                                                    *
  314. *  Good Luck...                                                     H. Hull *
  315. *                                                                           *
  316. ************************************************************************** */
  317.  
  318. /* Dynamic allocation routine for terrain growth arrays */
  319.  
  320. /* casts input arguments to their required types */
  321. /* typedef for UINT 'unsigned integer' is at top of page */
  322. /* cast the char pointer returned to type 'short *' for our use in Cell */
  323. /* calloc returns arg1 items of size arg2, cleared to zeros */
  324.  
  325. k = 0;
  326. do
  327.     {
  328.     if ((Cell[k] = (short *)calloc((UINT)(MAPMH*MAPW), (UINT)CSIZE)) == NULL)
  329.         {
  330.         exit(0);  /* Silently exits.  Anybody know a more graceful method? */
  331.     }
  332. }
  333.  
  334. /* MAPMH is module 'height' in the window; */
  335. /* an even number of modules stack together like blind slats on the window */
  336.  
  337. /* Each module produces a pointer to its base that goes into Cell[0] so   */
  338. /* we have to then fill the Cell array with window height-based pointers  */
  339. /* one pointer for each line across the window.  These serve as pointers  */
  340. /* to window-width based line data we want to put in the returned module. */
  341.  
  342. while ((k = k + MAPMH) < MAPH);
  343.  
  344. for (k = 1; k <= CAMOD; k++)
  345.     {
  346.     l = MAPMH*k;
  347.     m = MAPMH*(k-1);
  348.     for (j = m+1; j < l; j++)
  349.         {
  350.         Cell[j] = Cell[j-1] + MAPW;         /* makes an array of pointers */
  351.  
  352.     }
  353. }
  354.  
  355. /* Back to the original program now... */
  356.  
  357.     IntuitionBase = (struct IntuitionBase *)
  358.                             OpenLibrary("intuition.library", 0L);
  359.     if (IntuitionBase != NULL)
  360.         {
  361.         GfxBase = (struct GfxBase *)
  362.                           OpenLibrary("graphics.library", 0L);
  363.         if (GfxBase != NULL)
  364.             {
  365.             Screen = OpenScreen(&newScreen);
  366.             if (Screen != NULL)
  367.                 {
  368.                 LoadRGB4(&Screen->ViewPort, ColourMap, COLOURS);
  369.                 newWindow.Screen = Screen;
  370.                 Window = OpenWindow(&newWindow);
  371.                 if (Window != NULL)
  372.                     {
  373.                     DateStamp(&ds);
  374.                     Seed = (ds.ds_Minute ^ ds.ds_Tick) | 1;
  375.  
  376. /*  Ok.  I give up.  I notice that Cell[y][x] in main() is a place to put */
  377. /* a single data point, even though Cell is actually a singly subscripted */
  378. /* array of pointers.  We certainly wouldn't want to smash the pointer by */
  379. /* attempting an assignment of data to Cell[n].  The compiler would warn  */
  380. /* about a short to pointer conversion, anyway.  We could use *Cell[n] to */
  381. /* obtain the quantity in the base cell of any particular line of data,   */
  382. /* and pointer arithmetic (*Cell[y]+x) might (?) get us the data we want  */
  383. /* to access.  But when I try to use Cell[y][x] in a subroutine, I get an */
  384. /* excess array subscript error.  No amount of fiddling with subroutine   */
  385. /* argument definitions yielded any error free situation.  I can't use a  */
  386. /* 'static' definition in main for a dynamically allocated data element,  */
  387. /* either, can I?  So this program got hacked to where it looks like a    */
  388. /* meat packing plant after a company holiday.  However, I have now used  */
  389. /* Cell in the "show" subroutine.  Indentation is out the window in main. */
  390.  
  391. do
  392. {
  393.  
  394. /*
  395.  * grow - grow the basic scenery heights.
  396.  */
  397.  
  398.     Cell[0][0] = 0;
  399.     step = MAP;
  400.     for (i=0; i<SIZE; i++)
  401.         {
  402.         nextStep = step / 2;
  403.         for (l=0; l<MAPH; l+=step)
  404.             {
  405.             l1 = l + nextStep;
  406.             l2 = l + step;
  407.             if (l2 == MAPH)
  408.                 l2 = 0;
  409.             for (c=0; c<MAPW; c+=step)
  410.                 {
  411.                 c1 = c + nextStep;
  412.                 c2 = c + step;
  413.                 if (c2 == MAPW)
  414.                     c2 = 0;
  415.                 set(i, (Cell[l][c] + Cell[l][c2] + 1) / 2);
  416.                 Cell[l][c1] = altitude;
  417.                 set(i, (Cell[l][c] + Cell[l2][c] + 1) / 2);
  418.                 Cell[l1][c] = altitude;
  419.                 set(i, (Cell[l][c] + Cell[l][c2] +
  420.                                  Cell[l2][c] + Cell[l2][c2] + 2) / 4);
  421.                 Cell[l1][c1] = altitude;
  422.                 }
  423.             }
  424.         step = nextStep;
  425.         }
  426.  
  427. /*
  428.  * display - display the resulting scenery.
  429.  */
  430.  
  431. /* clear a band as tall as the highest relief color value */
  432.     for (l=0; l<COLOURS; l++)
  433.         {
  434.         (void) bound(l);
  435.     }
  436. /* call the Devil's Post Pile routine once for each line in the display */
  437. /* set cc to use height for colour */
  438.     cc = 0;
  439.     for (l=0; l<WINDOW_HEIGHT-1; l++)
  440.         {
  441.         (void) show(cc, l, Cell[l]);
  442.     }
  443. /* make a call for the the bottom line, but set the color to #2 */
  444.     cc = 2;
  445.     l = WINDOW_HEIGHT - 1;
  446.         (void) show(cc, l, Cell[l]);
  447.  
  448. /*
  449.  *  Now we go find out how the audience reacted.
  450.  */
  451.  
  452. interested = FALSE;
  453. reqWindow.Screen = Screen;
  454. if ((wleftedge >= 0) || (wtopedge >= 0))
  455.     {
  456.     reqWindow.LeftEdge = wleftedge;  /* if the window has been opened, */
  457.     reqWindow.TopEdge = wtopedge;    /* use the updated coordinates    */
  458. }
  459. RWindow = OpenWindow(&reqWindow);
  460. if (RWindow != NULL)
  461.     {
  462.     (void) InitRequester(&req);  /* Rumor: clears req structure entirely */
  463.  
  464.     /* Init the fields in the Requester structure */ 
  465.   
  466.     req.LeftEdge  = 4; 
  467.     req.TopEdge   = 12; 
  468.     req.Width     = 56; 
  469.     req.Height    = 59; 
  470.     req.ReqGadget = &ongad;         /* First gadget */
  471.     req.ReqText   = &text;          /* Text for enquirer */
  472.     req.BackFill  = 1;              /* BackGnd colour to window */
  473.     req.Flags     = 0; 
  474.     req.ReqBorder = &out_border;    /* Must have at least one */
  475.  
  476.     /* initialize the enquirer text */
  477.  
  478.     text.IText = (unsigned char *)"DONE";
  479.     postext.IText = (unsigned char *)"NEXT";
  480.     negtext.IText = (unsigned char *)"QUIT";
  481.  
  482.     if (Request(&req, RWindow) == 1)  /* try to open the enquirer window */
  483.         {
  484.         interested = (autoEnquire());  /* if we're here it did open, so  */
  485.                              /* now we can find out what they want to do */
  486.  
  487. /* To avoid some the above cumbersome initialization, the following is a
  488.     model for the AutoEnquire function (for v1.3 anybody?) which allows us
  489.     to specify WHERE the requester will go rather than HOW BIG it will be:
  490.  
  491.         interested = (AutoEnquire
  492.         (
  493.                     Window,
  494.                     BodyText,
  495.                     PositiveText,
  496.                     NegativeText,
  497.                     PositiveFlags,
  498.                     NegativeFlags,
  499.                     LeftEdge,
  500.                     TopEdge
  501.                     ));
  502.    It should also be possible to set things up so that if the user moves
  503.     the AutoEnquire window, Intuition can keep track of where it was put
  504.     so that it will open up in the same place next time it's called.
  505.     Would you like that, Bunky?  Since Intuition maintains the structure,
  506.     it could be done with appropriate flags.                           */
  507.  
  508.  
  509.         CloseWindow(RWindow);  /* if we're here we got their attention */
  510.                                /* and we can close the enquirer window */
  511.     }
  512. }
  513. }
  514. while (interested == TRUE);
  515.  
  516. /* Ok.  We're back to normal now.  We're also all done with the program. */
  517. /* Time to clean up the mess and get out while we're still ahead...      */
  518.  
  519.                     CloseWindow(Window);
  520.                 }
  521.                 CloseScreen(Screen);
  522.             }
  523.             CloseLibrary(GfxBase);
  524.         }
  525.         CloseLibrary(IntuitionBase);
  526.     }
  527. }
  528.  
  529.